/*
 * ringbuffer.c
 *
 *  Created on: 2017年2月27日
 *      Author: baishaoqi
 */

#include <stdlib.h>
#include <malloc.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

#include "st.h"
#define USE_LOCAL_LOG_LEVEL
#include "ringbuffer.h"
#include "log.h"

#pragma pack(4)
struct _ursax_ringbuf_rec
{
    //alloc为false，表示内存可以释放，为true，表示还在使用
    bool alloc;
    uint64_t length;
};
#pragma pack()

typedef struct _ursax_ringbuf_rec _ursax_ringbuf_rec;

struct ursax_ringbuf {
	bool inner_malloc;
	uint8_t * data;
	uint8_t * head;
	uint8_t * tail;
	uint64_t size;
	uint64_t alignment;
	uint64_t used;
	st_cond_t cond;
};

static inline uint64_t upper_align(uint64_t size, uint64_t align)
{
	return (size + align - 1) & (~(align - 1));
}

ursax_ringbuf * ursax_ringbuf_create (uint64_t ring_size, uint64_t alignment, void * ptr)
{
    //register_local_log_level("c.ringbuffer");

    // alignment不能<=1， 且得是2的幂， 不能小于sizeof(_ursax_ringbuf_rec)
    if (alignment < sizeof(_ursax_ringbuf_rec) || alignment <= 1 || (alignment & (alignment - 1)) != 0) {
        LOG_ERROR("ursax_ringbuf_init failed, alignment should be greater than or equal to sizeof(_ursax_ringbuf_rec) and also be power of 2");
    	return NULL;
    }
    // 调整alignment, 是它至少不小于sizeof(_ursax_ringbuf_rec)
    alignment = alignment < sizeof(_ursax_ringbuf_rec) ? sizeof(_ursax_ringbuf_rec) : alignment;
    ursax_ringbuf * ringbuf;
    ringbuf = (ursax_ringbuf *)malloc(sizeof(ursax_ringbuf));
    ring_size = upper_align(ring_size, alignment);
    if (ptr) {
        ringbuf->inner_malloc = false;
        ringbuf->data = (uint8_t *)ptr;
    } else {
	ringbuf->inner_malloc = true;
	ringbuf->data = (uint8_t *)memalign(alignment, ring_size);
    }
    ringbuf->alignment = alignment;
    ringbuf->size = ring_size;
    ringbuf->used = 0;
    ringbuf->head = ringbuf->data;
    ringbuf->tail = ringbuf->data;
    ringbuf->cond = st_cond_new();
    LOG_DEBUG("ursax_ringbuf_init size: %lld, used: %lld, head: %p , tail: %p", ringbuf->size, ringbuf->used, ringbuf->head, ringbuf->tail);
    return ringbuf;
}

void * ursax_ringbuf_destroy (ursax_ringbuf * ring)
{
	LOG_DEBUG("ursax_ringbuf_fini size: %lld, used: %lld, head: %p , tail: %p", ring->size, ring->used, ring->head, ring->tail);
	if (!ring) {
		return NULL;
	}
	void * outer_mem = NULL;
	if (ring->inner_malloc) {
		free(ring->data);
	} else {
		outer_mem = (void *)ring->data;
	}
	st_cond_destroy(ring->cond);
	free(ring);
	return outer_mem;
}

static void * _ursax_ringbuf_malloc (ursax_ringbuf *ring, uint64_t len)
{
    // 返回给用户的指针
    uint8_t * user_ptr = NULL;
    // 最后返回给用户的地址长度为user_len
    uint64_t user_len = upper_align(len, ring->alignment);
    // 实际占用还要加上头部，即alignment
    len = user_len + ring->alignment;
    uint8_t * end = ring->data + ring->size;

    if (ring->size - ring->used < len) {
    	return NULL;
    }

    if (ring->tail + len > end) {
    	if (ring->data + len > ring->head) {
    		return NULL;
    	}
    	// 至少够一个struct _ursax_ringbuf_rec占位, 因为一个_ursax_ringbuf_rec会分配alignment大小内存
    	_ursax_ringbuf_rec * tail = (_ursax_ringbuf_rec *)ring->tail;
    	tail->alloc = false;
    	tail->length = end - (ring->tail + ring->alignment);
        ring->used += tail->length + ring->alignment;
    	ring->tail = ring->data;
    }
    _ursax_ringbuf_rec * p = (_ursax_ringbuf_rec *)ring->tail;
    p->alloc = true;
    p->length = user_len;
    ring->used += len;
    user_ptr = ring->tail + ring->alignment;
    ring->tail = user_ptr + p->length;
    if (ring->tail == end)  {
    	ring->tail = ring->data;
    }
    //_ursax_ringbuf_print_record(ring);
    LOG_DEBUG("_ursax_ringbuf_malloc() size: %lld, used: %lld, head: %p , tail: %p, user_ptr: %p, user_ptr %% %d = %d, use_actual_space: %d",
        ring->size, ring->used, ring->head, ring->tail, user_ptr, ring->alignment,  (uintptr_t)user_ptr % ring->alignment, user_len);
    return user_ptr;
}

void * ursax_ringbuf_malloc (ursax_ringbuf * ring, uint64_t len)
{
    LOG_DEBUG("step into ursax_ringbuf_malloc");
    if (!ring || len == 0) {
        return NULL;
    }

    void * ptr = NULL;
    while ((ptr = _ursax_ringbuf_malloc(ring, len)) == NULL) {
        LOG_WARN("Cant't malloc now, capacity: %lld, used: %lld, alloc len %d", ring->size, ring->used, len);
	st_cond_wait(ring->cond);
    }
    return ptr;
}


uint64_t ursax_ringbuf_capacity(ursax_ringbuf *ring)
{
    return ring? ring->size : 0;
}

uint64_t ursax_ringbuf_used(ursax_ringbuf *ring)
{
    return ring? ring->used : 0;
}

void ursax_ringbuf_free (ursax_ringbuf * ring, void * buf)
{
    if (!ring || !buf || ((uintptr_t)((uint8_t *)buf)) % ring->alignment) {
        return;
    }

    _ursax_ringbuf_rec * rec = (_ursax_ringbuf_rec *)((uint8_t *)buf - ring->alignment);

    rec->alloc = false;

    uint8_t * end = ring->data + ring->size;
    while (ring->used > 0 && ((_ursax_ringbuf_rec *)ring->head)->alloc == false) {
    	_ursax_ringbuf_rec * head = (_ursax_ringbuf_rec *)ring->head;
    	ring->used -= head->length + ring->alignment;
    	ring->head += ring->alignment + head->length;
    	if (ring->head == end) ring->head = ring->data;
        LOG_DEBUG("ursax_ringbuf_free() rec to free info used: %d, buf: %p", head->length, buf);
        LOG_DEBUG("ursax_ringbuf_free() size: %lld, used: %lld, head: %p , tail: %p, rec to release: %p",
        		ring->size, ring->used, ring->head, ring->tail, (uint8_t *)rec);
        
    }
    st_cond_signal(ring->cond);
}
